home *** CD-ROM | disk | FTP | other *** search
/ EnigmA Amiga Run 1996 March / EnigmA AMIGA RUN 05 (1996)(G.R. Edizioni)(IT)[!][issue 1996-03][Skylink CD IV].iso / earcd / editor / chktex.lha / chktex / source / ChkTeX.c next >
C/C++ Source or Header  |  1996-01-25  |  21KB  |  894 lines

  1. /*
  2.  *  ChkTeX v1.2, finds typographic errors in (La)TeX files.
  3.  *  Copyright (C) 1995-96 Jens T. Berger Thielemann
  4.  *
  5.  *  This program is free software; you can redistribute it and/or modify
  6.  *  it under the terms of the GNU General Public License as published by
  7.  *  the Free Software Foundation; either version 2 of the License, or
  8.  *  (at your option) any later version.
  9.  *
  10.  *  This program is distributed in the hope that it will be useful,
  11.  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
  12.  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  13.  *  GNU General Public License for more details.
  14.  *
  15.  *  You should have received a copy of the GNU General Public License
  16.  *  along with this program; if not, write to the Free Software
  17.  *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  18.  *
  19.  *  Contact the author at:
  20.  *        Jens Berger
  21.  *        Spektrumvn. 4
  22.  *        N-0666 Oslo
  23.  *        Norway
  24.  *        E-mail: <jensthi@ifi.uio.no>
  25.  *
  26.  *
  27.  */
  28.  
  29. #include "ChkTeX.h"
  30.  
  31.  
  32.  
  33. struct WordList
  34.     Silent        = {0L},
  35.     Abbrev        = {0L},
  36.     Linker         = {0L},
  37.     IJAccent    = {0L},
  38.     Italic        = {0L},
  39.     UserWarn    = {0L},
  40.     CmdLine        = {0L},
  41.     PostLink    = {0L};
  42.  
  43. struct Stack
  44.     CharStack    = {0L};
  45.  
  46.  
  47. struct WordList *CurRead = NULL;        /* Where we'll put the words we find */
  48.  
  49. /************************************************************************/
  50.  
  51. const UBYTE BrOrder    [NUMBRACKETS + 1] = "()[]{}";
  52.  
  53. /******************** VARS CHANGED RUNTIME ******************************/
  54.  
  55. ULONG Brackets     [NUMBRACKETS];
  56.  
  57. BOOL    AtLetter,                /* Whether `@' is a letter or not.        */
  58.         MathMode;                /* Whether we're in math mode or not    */
  59.  
  60. ULONG    ErrPrint,                /* # errors printed                     */
  61.         WarnPrint,                /* # warnings printed                    */
  62.         UserSupp;                /* # user suppressed warnings            */
  63.  
  64. enum ItState                    /* Are we doing italics?                */
  65.         ItState;
  66.  
  67. /***************************** ERROR MESSAGES ***************************/
  68.  
  69. #define    INTFAULTMSG        "INTERNAL FAULT OCCURED! PLEASE SUBMIT A BUG REPORT!\n"
  70.  
  71. #ifndef __LOCALIZED
  72.     char InternFault [] =     INTFAULTMSG;
  73. #    define INTERNFAULT    &InternFault[0]
  74. #else
  75. #    define INTERNFAULT    INTFAULTMSG
  76. #endif
  77.  
  78. struct ErrMsg PrgMsgs [pmMaxFault + 1] =
  79. {
  80.     {pmMinFault,    etErr,    TRUE,
  81.         INTERNFAULT},
  82.     {pmUnknownTerm,    etErr,    TRUE,
  83.         "Unknown terminal type - using normal verbosity."},
  84.  
  85.     {pmNoFileMatch,    etWarn,    TRUE,
  86.         "No files matched the pattern `%s'."},
  87.     {pmNoTeXOpen,    etErr,    TRUE,
  88.         "Unable to open TeX files."},
  89.  
  90.     {pmRename,        etMsg,    TRUE,
  91.         "Renaming `%s' as `%s'."},
  92.     {pmRenameErr,    etErr,    TRUE,
  93.         "Could not rename `%s' to `%s'."},
  94.  
  95.     {pmOutOpen,        etErr,    TRUE,
  96.         "Unable to open output file."},
  97.     {pmOutTwice,    etErr,    TRUE,
  98.         "You can specify output file only once."},
  99.  
  100.     {pmStrDupErr,     etErr,    TRUE,
  101.         "Unable to duplicate strings - no memory?"},
  102.     {pmWordListErr,    etErr,    TRUE,
  103.         "Unable to create wordlist - no memory?"},
  104.     {pmNoStackMem,    etErr,    TRUE,
  105.         "Unable to create stack - no memory?\n"},
  106.  
  107.     {pmWarnNumErr,    etErr,    TRUE,
  108.         "Illegal warning number used."},
  109.     {pmVerbLevErr,    etErr,    TRUE,
  110.         "Illegal verbosity level."},
  111.  
  112.     {pmNotPSDigit,    etWarn,    TRUE,
  113.         "`%c' is not a %s digit - ignored!"},
  114.     {pmEscCode,        etWarn,    TRUE,
  115.         "Unknown escape code `%c%c' - ignored!"},
  116.  
  117.     {pmKeyWord,        etErr,    TRUE,
  118.         "Unsupported control word (`%s') encountered in file `%s'."},
  119.     {pmBraceCnt,    etErr,    TRUE,
  120. /*{*/    "ERROR: `%s' has a faulty format - too many }'s! Unable to read.\n"},
  121.     {pmRsrcOpen,    etWarn,    TRUE,
  122.         "Could not open `%s', may cause unwanted behaviour."},
  123.  
  124.  
  125.     {pmMaxFault,    etErr,    TRUE,
  126.         INTERNFAULT},
  127. };
  128.  
  129.  
  130. struct ErrMsg LaTeXMsgs [emMaxFault + 1] =
  131. {
  132.     {emMinFault,    etErr,    TRUE,
  133.         INTERNFAULT},
  134.     {emSpaceTerm,    etWarn, TRUE,
  135.         "Command terminated with space."},
  136.     {emNBSpace,        etWarn, TRUE,
  137.         "Non-breaking space (`~') should have been used."},
  138.     {emEnclosePar,    etWarn,    TRUE,
  139.         "You should enclose the previous parenthesis with `{}\'."},
  140.     {emItInNoIt,    etWarn, TRUE,
  141.         "Italic correction (`\\/') found in non-italic buffer."},
  142.     {emItDup,        etWarn, TRUE,
  143.         "Italic correction (`\\/') found more than once."},
  144.     {emNoItFound,    etWarn, TRUE,
  145.         "No italic correction (`\\/') found."},
  146.     {emAccent,        etWarn,    TRUE,
  147.         "Accent command `%s' needs use of `\\%c%s'."},
  148.     {emWrongDash,    etWarn, TRUE,
  149.         "Wrong length of dash may have been used."},
  150.     {emExpectC,        etWarn, TRUE,
  151.         "`%c' expected, found `%c'."},
  152.     {emSoloC,        etWarn, TRUE,
  153.         "Solo `%c' found."},
  154.     {emEllipsis,    etWarn, TRUE,
  155.         "You should use `\\dots' to achieve an ellipsis."},
  156.     {emInterWord,    etWarn, TRUE,
  157.         "Interword spacing (`\\ ') should been used."},
  158.     {emInterSent,    etWarn,    TRUE,
  159.         "Intersentence spacing (`\\@') should perhaps been used."},
  160.     {emNoArgFound,    etErr,    TRUE,
  161.         "Could not find argument for command `%s'."},
  162.     {emNoMatchC,    etWarn, TRUE,
  163.         "No match found for character `%c'."},
  164.     {emMathStillOn,    etWarn,    TRUE,
  165.         "Mathmode still on at end of LaTeX file."},
  166.     {emNoMatchCC,    etWarn,    TRUE,
  167.         "Number of `%c' doesn't match the number of `%c'!"},
  168.     {emUseQuoteLiga, etWarn, TRUE,
  169.         "Use either `` or '' as an alternative to `\"'."},
  170.     {emUseOtherQuote, etWarn, TRUE,
  171.         "Use \"'\" (ASCII 39) instead  of \"´\" (ASCII 180)."},
  172.     {emUserWarn, etWarn, TRUE,
  173.         "User-specified pattern found."},
  174.     {emNotIntended, etWarn, TRUE,
  175.         "This command might not be intended."},
  176.     {emComment, etMsg, FALSE,
  177.         "Comment displayed."},
  178.     {emThreeQuotes, etWarn, TRUE,
  179.         "Either %c\\,%c%c or %c%c\\,%c will look better."},
  180.     {emFalsePage, etWarn, TRUE,
  181.         "Delete this space to maintain correct pagereferences."},
  182.     {emEmbrace, etWarn, TRUE,
  183.         "You might wish to put this between a pair of `{}'"},
  184.     {emMaxFault,    etErr,    TRUE,
  185.         INTERNFAULT}                                         
  186. };
  187.  
  188. /************************************************************************/
  189.  
  190.  
  191. /*
  192.  * Have to do things this way, to ease some checking throughout the
  193.  * program.
  194.  */
  195.  
  196.  
  197. NEWBUF(TmpBuffer,    BUFLEN);
  198. NEWBUF(CmdBuffer,    BUFLEN);
  199. NEWBUF(ReadBuffer,    BUFLEN);
  200.  
  201. const STRPTR
  202.     Banner  =
  203.             "ChkTeX v1.2 - Copyright 1995-96 Jens T. Berger Thielemann\n",
  204.     BigBanner  =
  205.             "\n"
  206.             "ChkTeX comes with ABSOLUTELY NO WARRANTY; details on this and\n"
  207.             "distribution conditions in the GNU General Public License file.\n"
  208.             "Type \"ChkTeX -h\" for help, \"ChkTeX -i\" for distribution info.\n"
  209.             "To contact the author, write to:\n"
  210.             "Jens Berger, Spektrumvn. 4, N-0666 Oslo, Norway.\n"
  211.             "E-mail: <jensthi@ifi.uio.no>\n"
  212. #ifdef AMIGA
  213.             "Press Ctrl-\\ to abort stdin input.\n"
  214. #endif
  215.             ,
  216.     Distrib  =
  217.             "\n"
  218.             "This program is free software; you can redistribute it and/or modify\n"
  219.             "it under the terms of the GNU General Public License as published by\n"
  220.             "the Free Software Foundation; either version 2 of the License, or\n"
  221.             "(at your option) any later version.\n"
  222.             "\n"
  223.             "This program is distributed in the hope that it will be useful,\n"
  224.             "but WITHOUT ANY WARRANTY; without even the implied warranty of\n"
  225.             "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n"
  226.             "GNU General Public License for more details.\n"
  227.             "\n"
  228.             "You should have received a copy of the GNU General Public License\n"
  229.             "along with this program; if not, write to the Free Software\n"
  230.             "Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.\n"
  231.             "\n",
  232.     OnText  =
  233.             "On",
  234.     OffText  =
  235.             "Off",
  236.     HelpText  =
  237.             "\n"
  238.             "\n"
  239.             "                         Usage of ChkTeX v1.2\n"
  240.             "                         ~~~~~~~~~~~~~~~~~~~~\n"
  241.             "\n"
  242.             "                               Template\n"
  243.             "                               ~~~~~~~~\n"
  244.             "chktex [-hiqr] [-v[0|1|2]] [-l <rcfile>] [-[w|e|m|n] <[0-24]>]\n"
  245.             "       [-d <number>] [-o <outputfile>] [-b[0|1]] [-t[0|1]]\n"
  246.             "       [-x[0|1]] [-g[0|1]]\n"
  247.             "\n"
  248.             "----------------------------------------------------------------------\n"
  249.             "                       Description of options:\n"
  250.             "                       ~~~~~~~~~~~~~~~~~~~~~~~\n"
  251.             "Misc. options\n"
  252.             "~~~~~~~~~~~~~\n"
  253.             "    -h  --help      : This text.\n"
  254.             "    -i  --license   : Show distribution information\n"
  255.             "    -l  --localrc   : Read local .chktexrc formatted  file.\n"
  256.             "    -d  --debug     : Debug information. Give it a number.\n"
  257.             "    -r  --reset     : Reset settings to default.\n"
  258.             "\n"
  259.             "Muting warning messages:\n"
  260.             "~~~~~~~~~~~~~~~~~~~~~~~~\n"
  261.             "    -w  --warnon    : Makes msg # given a warning and turns it on.\n"
  262.             "    -e  --erroron   : Makes msg # given an error and turns it on.\n"
  263.             "    -m  --msgon     : Makes msg # given a message and turns it on.\n"
  264.             "    -n  --nowarn    : Mutes msg # given.\n"
  265.             "\n"
  266.             "Output control flags:\n"
  267.             "~~~~~~~~~~~~~~~~~~~~~\n"
  268.             "    -v  --verbosity : How errors are displayed.\n"
  269.             "                      Default 1, 0=Less, 2=More.\n"
  270.             "    -s  --splitchar : String used to split fields when doing -v0\n"
  271.             "    -o  --output    : Redirect error report to a file.\n"
  272.             "    -q  --quiet     : Shuts up about version information.\n"
  273.             "\n"
  274.             "Boolean switches (1 -> enables / 0 -> disables):\n"
  275.             "~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n"
  276.             "    -b  --backup    : Backup output file.\n"
  277.             "    -t  --tictoc    : Display a twirling baton.\n"
  278.             "                      -v0 does an -t0, too.\n"
  279.             "    -x  --wipeverb  : Ignore contents of `\\verb' commands.\n"
  280.             "    -g  --globalrc  : Read global .chktexrc file.\n"
  281.             "\n"
  282.             "----------------------------------------------------------------------\n"
  283.             "If no LaTeX files are specified on the command line, we will read from\n"
  284.             "stdin.   For explanation of warning/error messages, please consult the\n"
  285.             "main document ChkTeX.dvi or ChkTeX.ps.\n";
  286.  
  287.  
  288.  
  289. UBYTE EmptyString [] = "";
  290.  
  291.  
  292. /*
  293.  * Options we will set.
  294.  *
  295.  */
  296.  
  297. LONG Verbosity = vbNormal;
  298.  
  299.  
  300. BOOL     GlobalRC = TRUE, WipeVerb = TRUE, TicToc = TRUE, BackupOut = TRUE,
  301.         Quiet = FALSE, LicenseOnly = FALSE, UsingStdIn = FALSE;
  302.  
  303. LONG    DebugLevel = 0;
  304.  
  305. FILE *OutputFile = NULL, *InputFile = NULL;
  306.  
  307. STRPTR OutputName = EmptyString, InputName = EmptyString;
  308.  
  309. UBYTE    ConfigFile [BUFLEN] = RCFILE;
  310.  
  311. STRPTR    Delimit = ":";
  312.  
  313. STRPTR    PrgName;
  314.  
  315. /*
  316.  * End of config params.
  317.  */
  318.  
  319.  
  320. int main(int argc, char **argv)
  321. {
  322.     int        retval = EXIT_FAILURE, CurArg = 0L;
  323.     ULONG    LineCount, Count;
  324.  
  325.  
  326. #ifdef __LOCALIZED
  327.     InitStrings();
  328. #endif
  329.  
  330.     OutputFile = stdout;
  331.     PrgName = argv[0];
  332.  
  333.     SetupVars();
  334.     ReadRC(ConfigFile);
  335.  
  336.     if(CmdLine.Stack.Used)
  337.     {
  338.         ParseArgs(CmdLine.Stack.Used, (STRPTR *) CmdLine.Stack.Data);
  339.         CmdLine.Stack.Used = 0L;
  340.     }
  341.  
  342.     if(CurArg = ParseArgs((ULONG) argc, argv))
  343.     {
  344.         if(CmdLine.Stack.Used)
  345.         {
  346.             ParseArgs(CmdLine.Stack.Used, (STRPTR *) CmdLine.Stack.Data);
  347.             CmdLine.Stack.Used = 0L;
  348.         }
  349.  
  350.         if(!Quiet || LicenseOnly)
  351.             fprintf(stderr, Banner);
  352.  
  353.         if(((CurArg == argc) && !Quiet) || LicenseOnly)
  354.             fprintf(stderr, BigBanner);
  355.  
  356.         if(LicenseOnly)
  357.             fprintf(stderr, Distrib);
  358.         else
  359.         {
  360.             if(CurArg == argc)
  361.                 UsingStdIn = TRUE;
  362.  
  363.             for(Count = 0L;
  364.                 Count < Abbrev.Stack.Used;
  365.                 Count++)
  366.                 strupr(Abbrev.Stack.Data[Count]);
  367.  
  368.             if(DebugLevel)
  369.                 ShowIntStatus();
  370.  
  371.             if(OpenOut())
  372.             {
  373.                 for(;;)
  374.                 {
  375.                     for(Count = 0; Count < NUMBRACKETS; Count++)
  376.                         Brackets[Count] = 0L;
  377.  
  378.                     LineCount = 1;
  379.                     ItState  = itOff;
  380.                     AtLetter = MathMode  = FALSE;
  381.                     ErrPrint = WarnPrint = UserSupp = 0L;
  382.  
  383.                     if(UsingStdIn) {
  384.                         if(InputFile)
  385.                             break;
  386.                         else {
  387.                             InputFile = stdin;
  388.                             InputName = "stdin";
  389.                         }
  390.                     } else {
  391.                         if((CurArg <= argc) || InputName) {
  392.                             ifn(InputName = MatchFileName(NULL)) {
  393.                                 if(CurArg < argc)
  394.                                     InputName = MatchFileName(argv[CurArg++]);
  395.                             }
  396.  
  397.                             if(InputName)
  398.                                 InputFile = fopen(InputName, "r");
  399.                             else
  400.                                 break;
  401.  
  402.                             if(!InputFile) {
  403.                                 strcpy(TmpBuffer, InputName);
  404.                                 strcat(TmpBuffer, ".tex");
  405.  
  406.                                 ifn(InputFile = fopen(TmpBuffer, "r")) {
  407.                                     PrintPrgErr(pmNoTeXOpen);
  408.                                     break;
  409.                                 }
  410.                             }
  411.                         }
  412.                     }
  413.  
  414.                     if(InputFile && OutputFile)
  415.                     {
  416.                         if(Verbosity > vbSilent)
  417.                             fprintf(OutputFile, "Scanning %s...\n",
  418.                                     InputName);
  419.  
  420.                         while(!ferror(OutputFile) && !ferror(InputFile) &&
  421.                                 fgets(ReadBuffer, BUFLEN-1, InputFile))
  422.                         {
  423.  
  424.                             /* Make all spaces ordinary spaces */
  425.  
  426.                             strrep(ReadBuffer, '\n', ' ');
  427.                             strrep(ReadBuffer, '\r', ' ');
  428.                             strrep(ReadBuffer, '\t', ' ');
  429.  
  430.                             strcat(ReadBuffer, " ");
  431.                             FindErr(ReadBuffer, LineCount);
  432.  
  433.                             if((Verbosity != vbSilent) && TicToc)
  434.                             {
  435.                                 if(LineCount & 1)
  436.                                     tictoc();
  437.                             }
  438.                             LineCount++;
  439.                         }
  440.  
  441.                         PrintStatus(LineCount);
  442.                         retval = EXIT_SUCCESS;
  443.                     }
  444.                 }
  445.             }
  446.         }
  447.     }
  448.     exit(retval);
  449. }
  450.  
  451. /*
  452.  * Opens the output file handle & possibly renames
  453.  */
  454.  
  455. BOOL OpenOut(void)
  456. {
  457.     BOOL Success = TRUE;
  458.  
  459.     if(*OutputName)
  460.     {
  461.         if(BackupOut && fexists(OutputName))
  462.         {
  463.             strncpy(TmpBuffer, OutputName, BUFLEN - 4);
  464.             strcat(TmpBuffer, ".bak");
  465.  
  466.             if(fexists(TmpBuffer))
  467.                 remove(TmpBuffer);
  468.  
  469.             if(!rename(OutputName, TmpBuffer))
  470.                 PrintPrgErr(pmRename,
  471.                         OutputName, TmpBuffer);
  472.             else
  473.             {
  474.                 PrintPrgErr(pmRenameErr,
  475.                         OutputName, TmpBuffer);
  476.                 Success = FALSE;
  477.             }
  478.  
  479.         }
  480.  
  481.         if(Success)
  482.         {
  483.             ifn(OutputFile = fopen(OutputName, "w"))
  484.             {
  485.                 PrintPrgErr(pmOutOpen);
  486.                 Success = FALSE;
  487.             }
  488.         }
  489.     }
  490.     else
  491.         OutputFile = stdout;
  492.  
  493.     return(Success);
  494. }
  495.  
  496.  
  497. #define BOOLDISP(var)        ((var)? OnText : OffText)
  498. #define SHOWSTAT(text, arg)    fprintf(stderr, "\t" text ": %s\n", arg)
  499. #define BOOLSTAT(name, var)    SHOWSTAT(name, BOOLDISP(var))
  500. #define SHOWWORD(text, list)    fprintf(stderr, "\n" text "\n"); \
  501.                             for(Cnt = 0L; \
  502.                                 Cnt < list.Stack.Used; \
  503.                                 Cnt++) \
  504.                                 fprintf(stderr, "\t%s\n", list.Stack.Data[Cnt])
  505.  
  506. /*
  507.  * Prints some of the internal flags; mainly for debugging purposes
  508.  */
  509.  
  510. void ShowIntStatus(void)
  511. {
  512.     ULONG    Cnt;
  513.     STRPTR    String;
  514.  
  515.     fprintf(stderr, "There are %d warnings/error messages available:\n", emMaxFault - emMinFault - 1);
  516.  
  517.     for(Cnt = emMinFault + 1; Cnt < emMaxFault; Cnt++)
  518.     {
  519.         switch(LaTeXMsgs[Cnt].Type)
  520.         {
  521.         case etWarn:    String = "Warning"; break;
  522.         case etErr:        String = "Error"; break;
  523.         case etMsg:        String = "Message"; break;
  524.         }
  525.  
  526.         fprintf(stderr, "Number: %2d, Type: %s, Status: %s\n"
  527.                         "\tText: %s\n\n",
  528.                         Cnt, String,
  529.                         BOOLDISP(LaTeXMsgs[Cnt].InUse),
  530.                         LaTeXMsgs[Cnt].Message);
  531.     }
  532.  
  533.     fprintf(stderr, "Current flags include:\n");
  534.  
  535.     switch(Verbosity)
  536.     {
  537.     case vbSilent:    String = "Silent"; break;
  538.     case vbNormal:    String = "Normal"; break;
  539.     case vbFancy:    String = "Fancy"; break;
  540.     }
  541.  
  542.     SHOWSTAT("Verbosity", String);
  543.  
  544.     BOOLSTAT("Read global resource", GlobalRC);
  545.     BOOLSTAT("Wipe verbose commands", WipeVerb);
  546.     BOOLSTAT("Show twirling baton", TicToc);
  547.     BOOLSTAT("Backup outfile", BackupOut);
  548.     BOOLSTAT("Quiet mode", Quiet);
  549.     BOOLSTAT("Show license", LicenseOnly);
  550.     BOOLSTAT("Use stdin", UsingStdIn);
  551.  
  552.     SHOWWORD("`Silent' commands:", Silent);
  553.     SHOWWORD("Abbreviations searched for:", Abbrev);
  554.     SHOWWORD("`Linked' commands:", Linker);
  555.     SHOWWORD("Accenting commands needing `\\i' and `\\j':", IJAccent);
  556.     SHOWWORD("Italizing commands:", Italic);
  557.     SHOWWORD("User patterns:", UserWarn);
  558. }
  559.  
  560. /*
  561.  * Resets all flags (not wordlists) to their default values. Sets
  562.  * Outputfile to stdout.
  563.  *
  564.  */
  565.  
  566. void ResetSettings(void)
  567. {
  568.     GlobalRC = TRUE;
  569.     WipeVerb = TRUE;
  570.     TicToc = TRUE;
  571.     BackupOut = TRUE;
  572.     Quiet = FALSE;
  573.     LicenseOnly = FALSE;
  574.     UsingStdIn = FALSE;
  575.     DebugLevel = 0;
  576.  
  577.     Verbosity = vbNormal;
  578.  
  579.     if(OutputFile != stdout)
  580.     {
  581.         fclose(OutputFile);
  582.         OutputFile = stdout;
  583.     }
  584.  
  585.     if(InputFile)
  586.     {
  587.         fclose(InputFile);
  588.         InputFile = NULL;
  589.     }
  590.  
  591.     OutputName = EmptyString;
  592.     InputName = EmptyString;
  593. }
  594.  
  595. /*
  596.  * Reads a numerical argument from the argument. Supports concatenation
  597.  * of arguments (main purpose)
  598.  */
  599.  
  600. int ParseNumArg(LONG *Dest,            /* Where to put the value */
  601.                 LONG Default,        /* Value to put in if no in argue */
  602.                 STRPTR *Argument)    /* optarg or similar */
  603. {
  604.     if(Argument && *Argument && **Argument && isdigit(**Argument))
  605.         *Dest = strtol(*Argument, Argument, 10);
  606.     else
  607.         *Dest = Default;
  608.  
  609.     return(ShiftArg(Argument));
  610. }
  611.  
  612. /*
  613.  * Same as above; however, will toggle the boolean if user doesn't
  614.  * supply value
  615.  */
  616.  
  617. int ParseBoolArg(BOOL *Dest,            /* Boolean value */
  618.                  STRPTR *Argument)        /* optarg or similar */
  619. {
  620.     LONG    D = *Dest? 1L : 0L;
  621.     int        Retval;
  622.  
  623.     Retval = ParseNumArg(&D, *Dest? 0L : 1L, Argument);
  624.  
  625.     *Dest = D ? TRUE : FALSE;
  626.  
  627.     return(Retval);
  628. }
  629.  
  630. /*
  631.  * Returns the first character in the string passed, updates the
  632.  * string pointer, if the string is non-empty.
  633.  *
  634.  * 0 if the string is empty.
  635.  */
  636.  
  637. int ShiftArg(STRPTR *Argument)            /* optarg or similar */
  638. {
  639.     if(Argument && *Argument && **Argument)
  640.         return(*((STRPTR) (*Argument)++));
  641.     else
  642.         return 0;
  643. }
  644.  
  645. /*
  646.  * Parses an argv similar array.
  647.  */
  648.  
  649.  
  650. int ParseArgs(ULONG argc, char **argv)
  651. {
  652.     /* Needed for option parsing. */
  653.  
  654. static const
  655.     struct option long_options[] =
  656.     {
  657.         {"help",        no_argument,        0L,    'h'},
  658.         {"localrc",        required_argument,    0L,    'l'},
  659.         {"output",        required_argument,    0L,    'o'},
  660.         {"warnon",        required_argument,    0L,    'w'},
  661.         {"erroron",        required_argument,    0L,    'e'},
  662.         {"msgon",        required_argument,    0L, 'm'},
  663.         {"nowarn",        required_argument,    0L,    'n'},
  664.         {"verbosity",    optional_argument,    0L,    'v'},
  665.         {"debug",        required_argument,    0L, 'd'},
  666.         {"reset",        no_argument,        0L,    'r'},
  667.         {"quiet",        no_argument,        0L,    'q'},
  668.         {"license",        no_argument,        0L,    'i'},
  669.         {"splitchar",    required_argument,    0L,    's'},
  670.  
  671.         {"backup",        optional_argument,        0L,    'b'},
  672.         {"globalrc",    optional_argument,        0L,    'g'},
  673.         {"wipeverb",    optional_argument,        0L,    'x'},
  674.         {"tictoc",        optional_argument,        0L,    't'},
  675.         {0L,            0L,                        0L, 0L}
  676.     };
  677.  
  678.     int        option_index = 0L;
  679.  
  680.     int        c, nextc, ErrType;
  681.     LONG    Err;
  682.  
  683.     int        Retval = FALSE;
  684.     BOOL    Success = FALSE;
  685.  
  686.     int        InUse;
  687.  
  688.     enum
  689.     {
  690.         aeNoErr = 0,
  691.         aeArg,                /* missing/bad required argument */
  692.         aeOpt,                /* unknown option returned */
  693.         aeHelp,                /* just a call for help */
  694.         aeMem                /* no memory */
  695.     } ArgErr = aeNoErr;
  696.  
  697.     optind = 0;
  698.  
  699.     while(!ArgErr && ((c = getopt_long((int) argc, argv,
  700.                             "hl:o:w:e:m:n:v::d:rqib::g::x::t::s:", long_options,
  701.                             &option_index)) != EOF))
  702.     {
  703.         while(c) {
  704.             nextc = 0;
  705.             switch(c)
  706.             {
  707.             case 's':
  708.                 ifn(Delimit = strdup(optarg)) {
  709.                     PrintPrgErr(pmStrDupErr);
  710.                     ArgErr = aeMem;
  711.                 }
  712.  
  713.                 break;
  714.             case 'd':
  715.                 nextc = ParseNumArg(&DebugLevel, 0, &optarg);
  716.  
  717.                 break;
  718.             case 'i':
  719.                 LicenseOnly = TRUE;
  720.  
  721.                 nextc = ShiftArg(&optarg);
  722.                 break;
  723.             case 'q':
  724.                 Quiet = TRUE;
  725.  
  726.                 nextc = ShiftArg(&optarg);
  727.                 break;
  728.  
  729.                 /* Variation of Duff's device... */
  730.                 do {
  731.             case 'w':
  732.                     ErrType = etWarn;
  733.                     InUse = TRUE;
  734.                     break;
  735.             case 'e':
  736.                     ErrType = etErr;
  737.                     InUse = TRUE;
  738.                     break;
  739.             case 'm':
  740.                     ErrType    = etMsg;
  741.                     InUse = TRUE;
  742.                     break;
  743.             case 'n':
  744.                     InUse = FALSE;
  745.                 } while(FALSE);
  746.  
  747.                 nextc = ParseNumArg(&Err, -1, &optarg);
  748.  
  749.                 if(betw(emMinFault, Err, emMaxFault)) {
  750.                     LaTeXMsgs[Err].Type    = ErrType;
  751.                     LaTeXMsgs[Err].InUse = InUse;
  752.                 } else {
  753.                     ArgErr = aeOpt;
  754.                     PrintPrgErr(pmWarnNumErr);
  755.                 }
  756.                 break;
  757.  
  758.             case 'g':
  759.                 nextc = ParseBoolArg(&GlobalRC, &optarg);
  760.                 if(!GlobalRC) {
  761.                     Silent.Stack.Used     = 0L;
  762.                     Abbrev.Stack.Used     = 0L;
  763.                     Linker.Stack.Used     = 0L;
  764.                     IJAccent.Stack.Used = 0L;
  765.                     Italic.Stack.Used     = 0L;
  766.                     UserWarn.Stack.Used = 0L;
  767.                 }
  768.                 break;
  769.  
  770.             case 'r':
  771.                 ResetSettings();
  772.                 nextc = ShiftArg(&optarg);
  773.                 break;
  774.  
  775.             case 'l':
  776.                 if(optarg)
  777.                     ReadRC(optarg);
  778.                 break;
  779.  
  780.             case 'v':
  781.                 nextc = ParseNumArg(&Verbosity, vbFancy, &optarg);
  782.                 switch(Verbosity)
  783.                 {
  784.                 case vbSilent:
  785.                 case vbNormal:
  786.                     break;
  787.                 case vbFancy:
  788.                     if(TermType != tmUnknown) {
  789.                         Verbosity = vbFancy;
  790.                     } else {
  791.                         PrintPrgErr(pmUnknownTerm);
  792.                         Verbosity = vbNormal;
  793.                     }
  794.                     break;
  795.                 default:
  796.                     PrintPrgErr(pmVerbLevErr);
  797.                     ArgErr = aeArg;
  798.                     break;
  799.                 }
  800.                 break;
  801.  
  802.             case 'o':
  803.                 if(optarg)
  804.                 {
  805.                     if(*OutputName)
  806.                     {
  807.                         PrintPrgErr(pmOutTwice);
  808.                         ArgErr = aeOpt;
  809.                     }
  810.                     else
  811.                     {
  812.                         ifn(OutputName = strdup(optarg))
  813.                         {
  814.                             PrintPrgErr(pmStrDupErr);
  815.                             ArgErr = aeMem;
  816.                         }
  817.                     }
  818.                 }
  819.  
  820.                 break;
  821.  
  822.             case 't':
  823.                 nextc = ParseBoolArg(&TicToc, &optarg);
  824.                 break;
  825.             case 'x':
  826.                 nextc = ParseBoolArg(&WipeVerb, &optarg);
  827.                 break;
  828.             case 'b':
  829.                 nextc = ParseBoolArg(&BackupOut, &optarg);
  830.                 break;
  831.  
  832.             case 'h':
  833.             case '?':
  834.             default:
  835.                 ArgErr = aeHelp;
  836.                 break;
  837.             }
  838.             c = nextc;
  839.         }
  840.     }
  841.  
  842.     if((argc >= optind) && !strcmp(argv[optind], "?"))
  843.         ArgErr = aeHelp;
  844.  
  845.     if(ArgErr)
  846.     {
  847.         fputs(Banner, stderr);
  848.         fputs(BigBanner, stderr);
  849.         fputs(HelpText, stderr);
  850.         Success = FALSE;
  851.     }
  852.     else
  853.         Success = TRUE;
  854.  
  855.     if(Success)
  856.         Retval = optind;
  857.  
  858.     return(Retval);
  859. }
  860.  
  861. /*
  862.  * Outputs a program error.
  863.  */
  864.  
  865.  
  866. void PrintPrgErr(enum PrgErrNum Error, ...)
  867. {
  868.     STRPTR    Type;
  869.     va_list    MsgArgs;
  870.  
  871.     if(betw(pmMinFault, Error, pmMaxFault)) {
  872.         switch(PrgMsgs[Error].Type) {
  873.         case etWarn:
  874.             Type = "WARNING";
  875.             break;
  876.         case etErr:
  877.             Type = "ERROR";
  878.             break;
  879.         case etMsg:
  880.             Type = "NOTE";
  881.             break;
  882.         }
  883.         fprintf(stderr, "%s: %s -- ", PrgName, Type);
  884.  
  885.         va_start(MsgArgs, Error);
  886.         vfprintf(stderr, PrgMsgs[Error].Message, MsgArgs);
  887.         va_end(MsgArgs);
  888.         fputc('\n', stderr);
  889.     }
  890. }
  891.  
  892.  
  893.  
  894.